/*************************************************************************
 *
 * funkce low-level pristupu do dataflash
 * 
 * (c) Ales Povalac, alpov@alpov.net, http://www.alpov.net/
 * zalozeno na kodu ATMELu, COPYRIGHT (c) ATMEL Norway, 1996-2001
 * 
 *************************************************************************
 *
 * $Id: dataflash.c 128 2007-02-18 20:35:06Z alpov $
 * 
 *************************************************************************/         

#include <avr/io.h>
#include <inttypes.h>
#include <avr/pgmspace.h>
#include "dataflash.h"
#include "hw.h"


//Look-up table for these sizes ->       512k,   1M,   2M,   4M,   8M,  16M,  32M,  64M
const uint8_t DF_pagebits[] PROGMEM =  {    9,    9,    9,    9,    9,   10,   10,   11 }; //index of internal page address bits
const uint16_t DF_pagesize[] PROGMEM = {  264,  264,  264,  264,  264,  528,  528, 1056 }; //index of pagesizes
const uint16_t DF_pages[] PROGMEM =    {  256,  512, 1024, 2048, 4096, 4096, 8192, 8192 }; //index of number of pages

unsigned char PageBits;
unsigned int PageSize;
unsigned int PageSizeNorm;
unsigned int Pages;


/*****************************************************************************
******************************************************************************/
void DF_SPI_init(void)
{
    unsigned char index_copy;
    
    PORTB |= _BV(PB7) | _BV(PB6) | _BV(PB5);
    DF_CS_inactive();
    DDRB |= _BV(DDB7) | _BV(DDB5);                          //Set MOSI, SCK as outputs
    InitDataflashHw();                                      //nastaveni chipselectu
    
    SPSR = _BV(SPI2X);                                      //SPI double speed settings
    SPCR = _BV(SPE) | _BV(MSTR) | _BV(CPHA) | _BV(CPOL);    //Enable SPI in Master mode, mode 3, Fosc/4
    
    index_copy = (Read_DF_status() & 0x38) >> 3;
    
    PageBits = pgm_read_byte(&DF_pagebits[index_copy]);   //get number of internal page address bits from look-up table
    PageSize = pgm_read_word(&DF_pagesize[index_copy]);   //get the size of the page (in bytes)
    PageSizeNorm = (1 << (PageBits - 1));
    Pages = pgm_read_word(&DF_pages[index_copy]);
}


/*****************************************************************************
******************************************************************************/
unsigned char DF_SPI_RW(unsigned char output)
{
    unsigned char input;
    
    SPDR = output;                          //put byte 'output' in SPI data register
    while (!(SPSR & 0x80))
        ;                                   //wait for transfer complete, poll SPIF-flag
    input = SPDR;                           //read value in SPI data reg.
    
    return input;                           //return the byte clocked in from SPI slave
}       


/*****************************************************************************
******************************************************************************/
unsigned char Read_DF_status(void)
{
    unsigned char result;
    
    DF_CS_toggle();
    DF_SPI_RW(StatusReg);                   //send status register read op-code
    result = DF_SPI_RW(0x00);               //dummy write to get result
    DF_CS_inactive();
    
    return result;
}


/*****************************************************************************
******************************************************************************/
void DF_Access(unsigned char OpCode, unsigned int PageAdr)
{
    DF_CS_toggle();
    
    DF_SPI_RW(OpCode);                                      //transfer to buffer op-code
    DF_SPI_RW((unsigned char)(PageAdr >> (16 - PageBits))); //upper part of page address
    DF_SPI_RW((unsigned char)(PageAdr << (PageBits - 8)));  //lower part of page address
    DF_SPI_RW(0x00);                                        //don't cares
    
    DF_CS_toggle();
    
    while (!(Read_DF_status() & 0x80))
        ;                                                   //monitor the status register, wait until busy-flag is high
    
    DF_CS_inactive();
}


/*****************************************************************************
******************************************************************************/
void Buffer_Read(unsigned char OpCode, unsigned int IntPageAdr, unsigned int No_of_bytes, char *BufferPtr)
{
    unsigned int i;

    DF_CS_toggle();
    
    DF_SPI_RW(OpCode);                          //buffer 1 read op-code
    DF_SPI_RW(0x00);                            //don't cares
    DF_SPI_RW((unsigned char)(IntPageAdr >> 8));//upper part of internal buffer address
    DF_SPI_RW((unsigned char)(IntPageAdr));     //lower part of internal buffer address
    DF_SPI_RW(0x00);                            //don't cares
    for (i = 0; i < No_of_bytes; i++) {
        *(BufferPtr) = DF_SPI_RW(0x00);         //read byte and put it in AVR buffer pointed to by *BufferPtr
        BufferPtr++;                            //point to next element in AVR buffer
    }
    
    DF_CS_inactive();
}


/*****************************************************************************
******************************************************************************/
void Buffer_Write(unsigned char OpCode, unsigned int IntPageAdr, unsigned int No_of_bytes, char *BufferPtr)
{
    unsigned int i;

    DF_CS_toggle();
    
    DF_SPI_RW(OpCode);                          //buffer 1 write op-code
    DF_SPI_RW(0x00);                            //don't cares
    DF_SPI_RW((unsigned char)(IntPageAdr >> 8));//upper part of internal buffer address
    DF_SPI_RW((unsigned char)(IntPageAdr));     //lower part of internal buffer address
    for (i = 0; i < No_of_bytes; i++) {
        DF_SPI_RW(*(BufferPtr));                //write byte pointed at by *BufferPtr to dataflash buffer 1 location
        BufferPtr++;                            //point to next element in AVR buffer
    }
    
    DF_CS_inactive();
}


/*****************************************************************************
******************************************************************************/
unsigned char Page_Buffer_Compare(unsigned char OpCode, unsigned int PageAdr)
{
    unsigned char stat;
    
    DF_CS_toggle();
    
    DF_SPI_RW(OpCode);  
    DF_SPI_RW((unsigned char)(PageAdr >> (16 - PageBits))); //upper part of page address
    DF_SPI_RW((unsigned char)(PageAdr << (PageBits - 8)));  //lower part of page address and MSB of int.page adr.
    DF_SPI_RW(0x00);                                        // "dont cares"
    
    DF_CS_toggle();
    
    do {
        stat = Read_DF_status();
    } while (!(stat & 0x80));                               //monitor the status register, wait until busy-flag is high
    
    DF_CS_inactive();
    
    return (stat & 0x40);
}


/*****************************************************************************
******************************************************************************/
void Buffer_Fill(unsigned char OpCode, unsigned char Data)
{
    unsigned int i;

    DF_CS_toggle();
    
    DF_SPI_RW(OpCode);                          //buffer 1 write op-code
    DF_SPI_RW(0x00);                            //don't cares
    DF_SPI_RW(0);                               //upper part of internal buffer address
    DF_SPI_RW(0);                               //lower part of internal buffer address
    for (i = 0; i < PageSize; i++) {
        DF_SPI_RW(Data);                        //write byte pointed at by *BufferPtr to dataflash buffer 1 location
    }
    
    DF_CS_inactive();
}

